package com.lvhr.rpc.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.setting.dialect.Props;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 配置文件动态监听器
 */
public class ConfigWatcher {
    private final List<String> configFiles;
    private final Map<String, WatchMonitor> monitors = new ConcurrentHashMap<>();
    private final Consumer<Map<String, Object>> reloadCallback;
    private final Map<String, Timer> debounceTimers = new ConcurrentHashMap<>();

    /**
     * 监听器构造方法
     *
     * @param configFiles
     * @param reloadCallback
     */
    public ConfigWatcher(List<String> configFiles, Consumer<Map<String, Object>> reloadCallback) {

        this.configFiles = configFiles;
        this.reloadCallback = reloadCallback;
    }
//    public ConfigWatcher(List<String> configFiles, Consumer<Map<String, Object>> reloadCallback) {
//        this.configFiles = configFiles.stream()
//                .map(this::resolveResourcePath) // 路径解析
//                .collect(Collectors.toList());
//        this.reloadCallback = reloadCallback;
//    }

    /**
     * 路径解析
     *
     * @param resource
     * @return
     */
    private String resolveResourcePath(String resource) {
        try {
            URL url = getClass().getClassLoader().getResource(resource);
            if (url == null) throw new FileNotFoundException(resource);

            // 处理开发环境路径
            if ("file".equals(url.getProtocol())) {
                return URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
            }
            // 处理 JAR 内路径（需特殊处理）
            return resource;
        } catch (Exception e) {
            throw new RuntimeException("Resolve resource failed: " + resource, e);
        }
    }

    /**
     * 启动文件监听（必须显式调用）
     */
    public void start() {
//        Set<File> dirs = new LinkedHashSet<>();
//
//        configFiles.forEach(file -> {
//            if (file.startsWith("classpath:")) {
//                // JAR 内资源启用轮询检查
//                startJarPolling(file.replace("classpath:", ""));
//            } else {
//                File f = FileUtil.file(file);
//                if (f.exists()) {
//                    dirs.add(f.isFile() ? f.getParentFile() : f);
//                }
//            }
//        });
        Set<File> dirs = configFiles.stream()
                .filter(FileUtil::exist)
                .map(FileUtil::file)
                .map(File::getParentFile)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        dirs.forEach(dir -> {
            if (!monitors.containsKey(dir.getAbsolutePath())) {
                WatchMonitor monitor = WatchMonitor.create(dir, WatchMonitor.ENTRY_MODIFY);
                monitor.setWatcher(new FileModifyWatcher());
                monitor.start();
                monitors.put(dir.getAbsolutePath(), monitor);
                System.out.println("Watching config dir: " + dir);
            }
        });
    }

    /**
     * 停止监听并释放资源
     */
    public void stop() {
        monitors.values().forEach(WatchMonitor::close);
        monitors.clear();
        debounceTimers.values().forEach(Timer::cancel);
        debounceTimers.clear();
    }

    private class FileModifyWatcher implements Watcher {
        @Override
        public void onCreate(WatchEvent<?> event, Path currentPath) {
            ConfigWatcher.this.handleConfigChange("CREATE");
        }

        @Override
        public void onModify(WatchEvent<?> event, Path currentPath) {
            ConfigWatcher.this.handleConfigChange("MODIFY");
        }

        @Override
        public void onDelete(WatchEvent<?> event, Path currentPath) {
            ConfigWatcher.this.handleConfigChange("DELETE");
        }

        @Override
        public void onOverflow(WatchEvent<?> event, Path currentPath) {
            ConfigWatcher.this.handleConfigChange("OVERFLOW");
        }

//        private void handleChange(String eventType) {
//            System.out.println("Config change detected: " + eventType);
//
//            // 防抖处理（500ms）
//            if (debounceTimers.containsKey("global")) {
//                debounceTimers.get("global").cancel();
//            }
//
//            Timer timer = new Timer();
//            timer.schedule(new TimerTask() {
//                @Override
//                public void run() {
//                    reloadCallback.accept(reloadAllConfigs());
//                    debounceTimers.remove("global");
//                }
//            }, 500);
//            debounceTimers.put("global", timer);
//        }
    }


    private void handleConfigChange(String eventType) {
        // 去抖逻辑
        String key = "config_change";
        debounceTimers.compute(key, (k, timer) -> {
            if (timer != null) timer.cancel();
            Timer newTimer = new Timer();
            newTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Map<String, Object> newConfig = reloadAllConfigs();
                    reloadCallback.accept(newConfig);
                    System.out.println("Config updated by event: " + eventType);
                }
            }, 500); // 延迟500ms触发
            return newTimer;
        });
    }
    /**
     * 重新加载所有配置文件
     */
    private Map<String, Object> reloadAllConfigs() {
        Map<String, Object> newConfig = new LinkedHashMap<>();
        configFiles.forEach(file -> {
            if (FileUtil.exist(file)) {
                try {
                    Props props = new Props(file);
                    props.forEach((k, v) -> newConfig.put(k.toString(), v));
                } catch (Exception e) {
                    System.err.println("Failed to reload config: " + file);
                }
            }
        });
        return newConfig;
    }
    // JAR 内轮询检查
    private void startJarPolling(String resourcePath) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            long lastModified = getJarResourceModified(resourcePath);

            @Override
            public void run() {
                long current = getJarResourceModified(resourcePath);
                if (current > lastModified) {
                    lastModified = current;
                    ConfigWatcher.this.handleConfigChange("JAR_MODIFY");
                }
            }
        }, 0, 5000); // 每5秒检查一次
    }
    // 获取 JAR 内资源修改时间
    private long getJarResourceModified(String resource) {
        try {
            URL url = getClass().getClassLoader().getResource(resource);
            if (url != null) {
                URLConnection conn = url.openConnection();
                return conn.getLastModified();
            }
            return 0;
        } catch (IOException e) {
            return 0;
        }
    }
}



